package com.huwaiwai.disruptor.demo02;

import com.google.common.util.concurrent.AtomicDouble;
import org.junit.Test;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Created by huweiwei on 2017/4/12.
 */
public class Test02 {
    long time = 100000000L;
    MyThread myThread = new MyThread();

    @Test
    public void test01() {
        long start = System.currentTimeMillis();
        for (long i = 0; i < time; i++) {
            myThread.addUnLock();
        }
        System.out.println(System.currentTimeMillis() - start + " " + myThread.getSum());
    }//732 1.2109999986281374E8

    @Test
    public void test02() {
        long start = System.currentTimeMillis();
        for (long i = 0; i < time; i++) {
            myThread.addByLock();
        }
        System.out.println(System.currentTimeMillis() - start + " " + myThread.getSum());
    }//2533 1.2109999986281374E8

    @Test
    public void test03() throws InterruptedException {
        long start = System.currentTimeMillis();
        int threadCount = 2;  //6709 1.2109999986281374E8
//        int threadCount = 5; //4413 1.2109999986281374E8
//        int threadCount = 10; //4481 1.2109999986281374E8
//        int threadCount = 50;  //4310 1.2109999986281374E8
        CountDownLatch latch = new CountDownLatch(threadCount);
        for (int k = 0; k < threadCount; k++) {
            new Thread() {
                @Override
                public void run() {
                    try {
                        for (long i = 0; i < time / threadCount; i++) {
                            myThread.addByLock();
                        }
                    } finally {
                        latch.countDown();
                    }
                }
            }.start();
        }
        latch.await(50, TimeUnit.SECONDS);
        System.out.println(System.currentTimeMillis() - start + " " + myThread.getSum());
    }

    @Test
    public void test05() throws InterruptedException {
        long start = System.currentTimeMillis();
//        int threadCount = 2;  //8642 1.2109999986281374E8
//        int threadCount = 5; //4339 1.2109999986281374E8
//        int threadCount = 10; //4286 1.2109999986281374E8
        int threadCount = 50;  //4299 1.2109999986281374E8
        CountDownLatch latch = new CountDownLatch(threadCount);
        for (int k = 0; k < threadCount; k++) {
            new Thread() {
                @Override
                public void run() {
                    try {
                        for (long i = 0; i < time / threadCount; i++) {
                            myThread.addBySem();
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        latch.countDown();
                    }
                }
            }.start();
        }
        latch.await(50, TimeUnit.SECONDS);
        System.out.println(System.currentTimeMillis() - start + " " + myThread.getSum());
    }

    @Test
    public void test06() throws InterruptedException {
        long start = System.currentTimeMillis();
        int threadCount = 2;  //4959 1.2109999986281374E8
//        int threadCount = 5; //6444 1.2109999986281374E8
//        int threadCount = 10; //6280 1.2109999986281374E8
//        int threadCount = 50;  //6706 1.2109999986281374E8
        CountDownLatch latch = new CountDownLatch(threadCount);
        for (int k = 0; k < threadCount; k++) {
            new Thread() {
                @Override
                public void run() {
                    try {
                        for (long i = 0; i < time / threadCount; i++) {
                            myThread.addByCAS();
                        }
                    } finally {
                        latch.countDown();
                    }
                }
            }.start();
        }
        latch.await(50, TimeUnit.SECONDS);
        System.out.println(System.currentTimeMillis() - start + " " + myThread.getAtomic());
    }

    @Test
    public void test07() throws InterruptedException {
        long start = System.currentTimeMillis();
        int threadCount = 1;
//        int threadCount = 2;  //4813 1.2109999986281374E8
//        int threadCount = 5; //6968 1.2109999986281374E8
//        int threadCount = 10; //6280 1.2109999986281374E8
//        int threadCount = 50;  //6706 1.2109999986281374E8
        CountDownLatch latch = new CountDownLatch(threadCount);
        for (int k = 0; k < threadCount; k++) {
            new Thread() {
                @Override
                public void run() {
                    try {
                        for (long i = 0; i < time / threadCount; i++) {
                            myThread.addByAtomicDouble();
                        }
                    } finally {
                        latch.countDown();
                    }
                }
            }.start();
        }
        latch.await(50, TimeUnit.SECONDS);
        System.out.println(System.currentTimeMillis() - start + " " + myThread.getAtomic());
    }
}

class MyThread {
    Lock lock = new ReentrantLock();
    static Semaphore semaphore = new Semaphore(1);
    private volatile double sum = 0d;
    private double skip = 1.211d;
    final AtomicDouble atomicDouble = new AtomicDouble(sum);

    public void addByLock() {
        lock.lock();
        try {
            sum += skip;
        } finally {
            lock.unlock();
        }
    }

    public void addByCAS() {
        int i = 1;
        while (true) {
            while (true) {
                double oldSum = atomicDouble.get();
                if (atomicDouble.compareAndSet(oldSum, oldSum + skip)) {
                    return;
                } else {
                    break;
                }
            }
        }
    }

    public void addByAtomicDouble(){
        atomicDouble.addAndGet(skip);
    }

    public void addUnLock() {
        sum += skip;
    }

    public void addBySem() throws InterruptedException {
        semaphore.acquire();
        try {
            sum += skip;
        } finally {
            semaphore.release();
        }

    }

    public double getSum() {
        return sum;
    }
    public double getAtomic() {
        return atomicDouble.get();
    }
}
